package com.chen.boot.config;

import com.alibaba.fastjson.JSON;
import com.chen.common.Dates;
import com.chen.common.Lang;
import com.chen.common.LocalUtils;
import com.chen.common.Result;
import com.chen.common.Utils;
import com.chen.boot.db.entity.ClientInfo;
import com.chen.boot.db.entity.ContactsInfo;
import com.chen.im.view.ContactsList;
import com.chen.net.SocketGroup;
import com.chen.pojo.NetInfoVo;
import com.chen.pojo.SocketMsgVo;
import com.chen.boot.service.ClientInfoService;
import com.chen.boot.service.ContactsInfoService;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * @Description
 * @Author Pengwei
 * @CreatDate 2021/11/16 23:10
 */
@Getter
@Slf4j
@Configuration
@EnableScheduling
public class ClientConfig {
    //个人默认用户名
    @Value("${client.default.name:big4}")
    String clientName;
    //本地IP
    @Value("${client.local.ip:}")
    List<String> localIps;
    @Resource
    ClientInfoService clientInfoService;
    @Resource
    NetInfoVo netInfoVo;
    @Value("${server.port}")
    String serverPort;
    @Value("${server.servlet.context-path}")
    String contextPath;
    @Resource
    ContactsInfoService contactsInfoService;
    @Value("${client.offline-answer-time}")
    Long offlineAnswerTime;

    private SocketGroup socketGroup;
    private List<String> clientIps;
    private ClientInfo clientInfo;

    private RestTemplate restTemplate = new RestTemplate();
    private ParameterizedTypeReference<Result<ClientInfo>> checkBackType = new ParameterizedTypeReference<Result<ClientInfo>>() {
    };

    /**
     * 初始化个人信息
     *
     * @return
     */
    @Bean
    public ClientInfo getUserInfo() {
        //查询本人信息
        clientInfo = clientInfoService.getOne(null);
        if (clientInfo == null) {
            clientInfo = new ClientInfo();
            clientInfo.setClientName(clientName);
            clientInfoService.save(clientInfo);
        }
        return clientInfo;
    }

    @SneakyThrows
    @PostConstruct
    public void initSocket() {
        //获取本地的IP,mac地址信息
        clientIps = LocalUtils.getLocalIPs();
        clientIps = clientIps.stream().distinct().filter(s -> !localIps.contains(s)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(clientIps)) {
            throw new RuntimeException(Lang.get("sys.local.ip.error"));
        }
        this.socketGroup = new SocketGroup(netInfoVo);
        //初始化监听器,得到回调
        socketGroup.initListener(message -> {
            if (StringUtils.isBlank(message)) {
                log.error("error: Received blank message.");
                return;
            }
            SocketMsgVo socketMsgVo = JSON.parseObject(message, SocketMsgVo.class);
            if (socketMsgVo == null) {
                log.error("error: wrong message.");
                return;
            }
            //多线程验证链接是否有效
            checkIp(socketMsgVo);
            GlobalListener.instance.listenerEvent(GlobalType.received, socketMsgVo);
        });
    }

    private void checkIp(SocketMsgVo socketMsgVo) {
        //过滤本地IP
        List<String> IPs = socketMsgVo.getIPs();
        //List<String> IPs = socketMsgVo.getIPs().stream().filter(ip -> !clientIps.contains(ip) && !localIps.contains(ip)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(IPs)) {
            return;
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity request = new HttpEntity<>(headers);
        //check出有效IP
        for (String ip : IPs) {
            String url = Utils.format("http://{}:{}{}/checkConnect", ip, serverPort, contextPath);
            //Result<ClientInfo> body = restTemplate.postForEntity(url, request, new ParameterizedTypeReference<Result<ClientInfo>>(){}).getBody();
            Result<ClientInfo> body = restTemplate.exchange(url, HttpMethod.POST, request, checkBackType).getBody();
            //检查成功,增加进联系人列表
            if (body != null && body.isOk() && body.getData() != null) {
                try {
                    contactsInfoService.updateContactsBySocket(ip, body.getData().getId(), body.getData().getClientName());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //添加定时任务,每隔10秒,发起心跳,用于维护自己在线
    @Scheduled(cron = "0/10 * * * * ?")
    private void configureTasks() {
        if (socketGroup == null) {
            throw new RuntimeException(Lang.get("sys.scoket.init.error"));
        }

        new Thread(() -> {
            SocketMsgVo socketMsgVo = new SocketMsgVo();
            socketMsgVo.setIPs(clientIps);
            socketMsgVo.setClientName(clientInfo.getClientName());
            socketMsgVo.setClientId(clientInfo.getId());
            try {
                String message = JSON.toJSONString(socketMsgVo);
                socketGroup.sendMsg(message);
                GlobalListener.instance.listenerEvent(GlobalType.heartbeat, message);
            } catch (IOException e) {
                log.error(Utils.format("error: ") + e);
            }
        }).start();
    }

    //添加定时任务,每个30秒清除不在线的客户端
    @Scheduled(cron = "0/30 * * * * ?")
    private void clearOffLine() {
        long offlineTime = System.currentTimeMillis() - offlineAnswerTime;
        Iterator<ContactsInfo> iterator = ContactsList.items.iterator();
        while (iterator.hasNext()) {
            ContactsInfo contactsInfo = iterator.next();
            long lastCheckTime = Dates.asDate(contactsInfo.getLastCheckTime()).getTime();
            if (lastCheckTime < offlineTime) {
                iterator.remove();
                GlobalListener.instance.listenerEvent(GlobalType.contactsOffLine, contactsInfo);
            }
        }
    }
}
